package com.umakr.ax.security.core.filter;

import com.google.common.collect.Maps;
import com.umakr.ax.security.core.SecurityStatusException;
import com.umakr.ax.security.core.jwt.JwtConfigProperties;
import com.umakr.ax.security.core.jwt.JwtTokenUtils;
import com.umakr.ax.security.service.SecurityUserDetailsServiceImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * @author gx
 * @since 2017/5/22
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private SecurityUserDetailsServiceImpl securityUserDetailsServiceImpl;
    @Autowired
    JwtTokenUtils jwtTokenUtils;
    @Autowired
    JwtConfigProperties jwtConfigProperties;

    private static final String BADCREDENTIALS_EXCEPTION_EXPIRED = "expired";
    private static final String BADCREDENTIALS_EXCEPTION_PDEXPIRED = "pdExpired";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = request.getHeader(jwtConfigProperties.getHeader());
        boolean skip = request.getRequestURI().contains("/api/system/user/login");
        if (authHeader != null && authHeader.startsWith(jwtConfigProperties.getHeadToken()) && !skip) {
            // The part after "Bearer "
            final String authToken = authHeader.substring(jwtConfigProperties.getHeadToken().length());
            String key = jwtConfigProperties.getSecret();
            try{
                Jws<Claims> jws =  Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
                String subject = jws.getBody().getSubject();
                if (subject != null && SecurityContextHolder.getContext().getAuthentication() == null) {

                    UserDetails userDetails = this.securityUserDetailsServiceImpl.loadUserByUsername(subject);
                    Map<String,Object> map = Maps.newHashMap();
                    if (jwtTokenUtils.validateToken(authToken, userDetails,map,request.getRequestURI())) {
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                                request));
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                    if(!map.isEmpty()){
                        if(map.get(BADCREDENTIALS_EXCEPTION_EXPIRED) != null){
                            throw new BadCredentialsException("token expired");
                        }
                        if(map.get(BADCREDENTIALS_EXCEPTION_PDEXPIRED) != null){
                            throw new BadCredentialsException("password has resetted");
                        }
                    }
                }
            }catch (ExpiredJwtException eje){ //超时。
                response.getWriter().write(SecurityStatusException.tokenExpired());
                return;
            }catch (Exception e){
                throw new BadCredentialsException(e.getLocalizedMessage());
            }
        }
        filterChain.doFilter(request, response);
    }
}
